﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Xml.Linq;
using GeoAPI.Geometries;
using GisSharpBlog.NetTopologySuite.Geometries;
using GisSharpBlog.NetTopologySuite.IO;

namespace AzukiMap.Tools
{
    public class XmlCommon
    {
        private readonly NumberFormatInfo _nfi = new NumberFormatInfo() {NumberDecimalSeparator = "."};

        # region ToXmls

        public XElement BrushToXml(string wrapElement, Brush brush)
        {
            if (brush == null)
            {
                return null;
            }
            return
                new XElement(
                    wrapElement,
                    BrushToXml(brush)
                    );
        }

        public XElement BrushToXml(Brush brush)
        {
            if (brush == null)
            {
                return null;
            }
            var brushElement = new XElement("Brush");
            if (brush.GetType() == typeof (SolidColorBrush))
            {
                var sBrush = (SolidColorBrush) brush;
                var solidColorElement = new XElement("SolidColorBrush",
                                                     ColorToXml(sBrush.Color),
                                                     new XElement("Opacity", sBrush.Opacity)
                    );
                brushElement.Add(solidColorElement);
            }
            return brushElement;
        }

        public XElement ColorToXml(string wrapElement, Color color)
        {
            return
                new XElement(
                    wrapElement,
                    ColorToXml(color)
                    );
        }

        public XElement ColorToXml(Color color)
        {
            var colorElement =
                new XElement("Color",
                             new XAttribute("R", color.R),
                             new XAttribute("G", color.G),
                             new XAttribute("B", color.B),
                             new XAttribute("A", color.A)
                    );
            return colorElement;
        }

        public XElement DoubleCollectionToXml(string wrapElement, DoubleCollection dc)
        {
            if (dc == null)
            {
                return null;
            }
            return
                new XElement(
                    wrapElement,
                    DoubleCollectionToXml(dc)
                    );
        }

        public XElement DoubleCollectionToXml(DoubleCollection dc)
        {
            if (dc == null)
            {
                return null;
            }
            var dcElement = new XElement("DoubleCollection",
                                         from d in dc
                                         select new XElement("Value", d.ToString(_nfi))
                );
            return dcElement;
        }

        public XElement StringListToXml(XName elemName, XName childElemName, List<string> list)
        {
            if (list == null)
            {
                return null;
            }
            var listElem = new XElement(elemName);
            foreach (string value in list)
            {
                listElem.Add(new XElement(childElemName, value));
            }
            return listElem;
        }

        public XElement ImageToXml(string wrapElement, Image img)
        {
            if (img == null)
            {
                return null;
            }
            return
                new XElement(
                    wrapElement,
                    ImageToXml(img)
                    );
        }

        public XElement ImageToXml(Image img)
        {
            if (img == null)
            {
                return null;
            }
            var imgElement =
                new XElement("Image",
                             new XAttribute("Width", img.Width),
                             new XAttribute("Height", img.Height),
                             new XElement("ImageSource",
                                          ImageSourceToXml(img.Source)
                                 ),
                             new XAttribute("Opacity", img.Opacity)
                    );
            return imgElement;
        }

        public XElement ImageSourceToXml(ImageSource source)
        {
            if (source == null)
            {
                return null;
            }
            if (source.GetType() == typeof (BitmapImage))
            {
                var bitmap = (BitmapImage) source;
                var sourceElement =
                    new XElement("BitmapImage",
                                 new XAttribute("Uri", bitmap.UriSource)
                        );
                return sourceElement;
            }
            else // DrawingImage
            {
                // DrawingImage is not implemented on Silverlight.
                var sourceElement = new XElement("DrawingImage");
                return sourceElement;
            }
        }

        public XElement CreateElement(string elementName, object value)
        {
            XElement element;
            if (value == null)
            {
                element = new XElement(elementName, "");
            }
            else
            {
                element = new XElement(elementName, value);
            }
            return element;
        }

        public XAttribute CreateAttr(string attrName, object value)
        {
            XAttribute attr;
            if (value == null)
            {
                //attr = new XAttribute(attrName, "");
                attr = null;
            }
            else
            {
                attr = new XAttribute(attrName, value);
            }
            return attr;
        }


        public XElement GeometryToXml(string elementName, IGeometry geometry)
        {
            XElement polygonXml;
            if (geometry == null)
            {
                //polygonXml = new XElement(elementName, "");
                polygonXml = null;
            }
            else
            {
                polygonXml = new XElement(elementName, geometry.ToString());
                polygonXml.SetAttributeValue("srid", geometry.SRID);
            }
            return polygonXml;
        }

        public XElement EnvelopeToXml(string elementName, IEnvelope envelope)
        {
            if (envelope == null)
            {
                return null;
            }
            var envelopeXml = new XElement(elementName);
            envelopeXml.Add(
                CreateElement("MinX", envelope.MinX),
                CreateElement("MinY", envelope.MinY),
                CreateElement("MaxX", envelope.MaxX),
                CreateElement("MaxY", envelope.MaxY)
                );
            return envelopeXml;
        }

        public IEnvelope GetEnvelopeElement(XElement parentElem, string elementName)
        {
            XElement envelopeElem = parentElem.Element(elementName);

            double minX, minY, maxX, maxY;
            try
            {
                minX = GetDoubleElement(envelopeElem, "MinX");
                minY = GetDoubleElement(envelopeElem, "MinX");
                maxX = GetDoubleElement(envelopeElem, "MinX");
                maxY = GetDoubleElement(envelopeElem, "MinX");
            }
            catch (Exception e)
            {
                return null;
            }

            IEnvelope envelope = new Envelope(minX, maxX, minY, maxY);

            return envelope;
        }

        # endregion

        #region parse Xmls

        public string GetStringAttr(XElement element, string attrName)
        {
            if (HasAttribute(element, attrName))
                return element.Attribute(attrName).Value;

            return "";
        }

        public int GetIntAttr(XElement element, string attrName)
        {
            if (HasAttribute(element, attrName))
                return int.Parse(element.Attribute(attrName).Value);

           return 0;
        }

        public double GetDoubleAttr(XElement element, string attrName)
        {
            if (HasAttribute(element, attrName))
            {
                var attr = element.Attribute(attrName).Value;
                if ( attr != "NaN")
                    try
                    {
                        return double.Parse(attr);
                    } catch
                    {
                        return double.NaN;
                    }
                else
                    return double.NaN;
            }

            return double.NaN;
        }

        public byte GetByteAttr(XElement element, string attrName)
        {
            if ( HasAttribute(element, attrName))
                return byte.Parse(element.Attribute(attrName).Value);

            return 0;
        }

        public bool GetBoolAttr(XElement element, string attrName)
        {
            bool value;
            try
            {
                value = bool.Parse(element.Attribute(attrName).Value);
            }
            catch (NullReferenceException ex)
            {
                return false;
            }
            return value;
        }

        public object GetEnumAttr(XElement element, string attrName, Type enumType)
        {
            if ( HasAttribute(element, attrName))
            {
                object enumVal = 
                    Enum.Parse(enumType, element.Attribute(attrName).Value, true);
                return enumVal;
            }
            return null;
        }

        public string GetStringElement(XElement parentElem, string elemName)
        {
            if ( HasElement(parentElem, elemName))
                 return parentElem.Element(elemName).Value;

            return string.Empty;
        }

        public int GetIntElement(XElement parentElem, string elemName)
        {
            if ( HasElement(parentElem, elemName))
                 return int.Parse(parentElem.Element(elemName).Value);

            return 0;
        }

        public double GetDoubleElement(XElement parentElem, string elemName)
        {
            if ( HasElement(parentElem, elemName))
            return double.Parse(parentElem.Element(elemName).Value);

            return double.NaN;
        }

        public bool GetBoolElement(XElement parentElem, string elemName)
        {
            if ( HasElement(parentElem, elemName))
            return bool.Parse(parentElem.Element(elemName).Value);

            return false;
        }


        public Color GetColorElement(XElement parentElem)
        {
            if (HasElement(parentElem, "Color"))
            {
                var c = new Color();
                XElement colorElem = parentElem.Element("Color");
                c.R = GetByteAttr(colorElem, "R");
                c.G = GetByteAttr(colorElem, "G");
                c.B = GetByteAttr(colorElem, "B");
                c.A = GetByteAttr(colorElem, "A");
                return c;
            }

            return Colors.Black;
        }

        public Brush GetBrushElement(XElement parentElem)
        {
            if (HasElement(parentElem, "Brush"))
            {
                XElement brushElem = parentElem.Element("Brush");

                // TODO: image brush
                XElement solidColorElem = brushElem.Element("SolidColorBrush");
                var brush = new SolidColorBrush();
                Color c = GetColorElement(solidColorElem);
                double opacity = GetDoubleElement(solidColorElem, "Opacity");
                brush.Color = c;
                brush.Opacity = opacity;

                return brush;
            }

            return null;
        }

        public IGeometry GetGeomElement(XElement parentElem, string elemName)
        {
            if (HasElement(parentElem, elemName))
            {
                XElement geomElem = parentElem.Element(elemName);
                int srid = -1;
                if ( geomElem.Attributes("srid").Count() > 0)
                {
                    srid = (int) geomElem.Attribute("srid");
                }

                IGeometry geom;
                if (!string.IsNullOrEmpty(geomElem.Value))
                {
                    IGeometryFactory gFactory = new GeometryFactory();
                    var reader = new WKTReader(gFactory);
                    geom = reader.Read(geomElem.Value);
                    geom.SRID = srid;
                }
                else
                {
                    geom = null;
                }
                return geom;
            }

            return null;
        }

        public object GetEnumElement(XElement parentElem, string elemName, Type enumType)
        {
            if (HasElement(parentElem, elemName))
            {
                XElement enumElem = parentElem.Element(elemName);

                string value = enumElem.Value;
                object enumVal = Enum.Parse(enumType, value, true);

                return enumVal;
            }

            return null;
        }

        public Uri GetUriElement(XElement parentElem, string elemName)
        {
            if (HasElement(parentElem, elemName))
            {
                XElement elem = parentElem.Element(elemName);
                string uriStr = elem.ToString();
                Uri uri;
                if (uriStr == null || uriStr == "")
                {
                    return null;
                }
                try
                {
                    uri = new Uri(uriStr, UriKind.RelativeOrAbsolute);
                }
                catch (Exception e)
                {
                    return null;
                }
                return uri;
            }

            return null;
        }

        public Image GetImageElement(XElement parentElem, string elemName)
        {
                var image = new Image();
            if (HasElement(parentElem, elemName))
            {
                XElement elem = parentElem.Element(elemName);
                XElement imgElem = elem.Element("Image");
                Uri imageSource = GetImageSourceElement(imgElem);

                if (imageSource == null)
                    return image;

                image.Width = GetDoubleAttr(imgElem, "Width");
                image.Height = GetDoubleAttr(imgElem, "Height");
                image.Source = new BitmapImage(imageSource);

                return image;
            }

            return image;
        }

        public Uri GetImageSourceElement(XElement parentElem)
        {
            if (HasElement(parentElem, "BitmapImage"))
            {
                XElement source = parentElem.Element("BitmapImage");
                if (source == null)
                {
                    return null;
                }
                string uriStr = source.Attribute("Uri").Value;
                var imageSource = new Uri(uriStr, UriKind.RelativeOrAbsolute);
                return imageSource;
            }

            return null;
        }

        /*
        public XElement ImageToXml(Image img)
        {
            XElement imgElement =
                new XElement("Image",
                    new XAttribute("Width", img.Width),
                    new XAttribute("Height", img.Height),
                    new XElement("ImageSource",
                        ImageSourceToXml(img.Source)
                        ),
                    new XAttribute("Opacity", img.Opacity)
                    );
            return imgElement;
        }

        public XElement ImageSourceToXml(ImageSource source)
        {
            if (source.GetType() == typeof(BitmapImage))
            {
                BitmapImage bitmap = (BitmapImage)source;
                XElement sourceElement =
                    new XElement("BitmapImage",
                        new XAttribute("Uri", bitmap.UriSource)
                        );
                return sourceElement;
            }
            else // DrawingImage
            {
                // DrawingImage is not implemented on Silverlight.
                XElement sourceElement = new XElement("DrawingImage");
                return sourceElement;
            }

        }
         */

        public DoubleCollection GetDoubleCollection(XElement parentElem, string elemName)
        {
            if (HasElement(parentElem, elemName))
            {
                XElement elem = parentElem.Element(elemName);
                var dc = new DoubleCollection();
                foreach (XElement v in elem.Elements())
                {
                    double value = double.Parse(v.Value);
                    dc.Add(value);
                }
                return dc;
            }

            return null;
        }

        public void SetElementValuesToList(List<string> list, IEnumerable<XElement> elements, string targetElementName)
        {
            foreach (XElement elem in elements)
            {
                if (elem.Name.ToString() == targetElementName)
                {
                    list.Add(elem.Value);
                }
            }
        }

        public Dictionary<string, string> SetElementValuesToDict(IEnumerable<XElement> elements)
        {
            var dict = new Dictionary<string, string>();
            foreach (XElement elem in elements)
            {
                dict.Add(elem.Name.ToString(), elem.Value);
            }
            return dict;
        }

        private bool HasAttribute(XElement element, string attrName)
        {
            if (element != null)
                if (element.Attributes(attrName).Count() == 1)
                    return true;
            
            return false;
        }

        bool HasElement(XElement element, string elemName)
        {
            if ( element != null )
                if ( element.Elements(elemName).Count() == 1)
                    return true;

            return false;
        }

        #endregion
    }
}